/*-------------------------------------------------------------------------*/
/**
  @file		execrefine_ref.c
  @author	Renaud Wanschoor
  @date	july 2004
  @version	$Revision: 2.4 $
  @brief	Boundary management library.
  @update date	March 2007 by Xiaochen Wu

  Library for the storage of vertex reassigment information
  This is dependant of the structure, current structure is one array per processor
  (called containers) each array contains boundary vertices of this processor, one vertex
  may have multiple entries in this array. Multuple entries of one vertex are linked in a
  double linked list. This library provides management functions for this structure 
  
*/
/*--------------------------------------------------------------------------*/
#include "pagrid.h"
#include "execrefine_params.h"

/**
 * \brief Build boundaries.
 *
 * allocate containers and fill them with boundary vertices while computing
 * refinement info. Each container need to be free by VREF_Free().
 * \param ctrl uncoarsening ctrl structure
 * \return the number of boundary vertices
 */
int VREF_Build(RefCtrl* ctrl){
  int i,v,w,j,jstart,jend,prov,prow,isbnd,to;

  int nbbnds    = 0;
  int nvtxs     = ctrl->g->nvtxs;
  int* adjidx   = ctrl->g->adjidx;
  int* adjncy   = ctrl->g->adjncy;
  int* adjwgt   = ctrl->g->adjwgt;
  int* pro      = ctrl->g->pro;

  int nnpro     = ctrl->n->npro;
  
  int* procs    = ctrl->procs;
  int* procon   = ctrl->procon;
  vRefCont* proref = ctrl->proref;
  vRef* bnds    = ctrl->bnds;

  /**********************/
  /* Initialize         */
  for(i=0;i<nnpro;i++)
    CONT_Ini(ctrl,proref + i);
  for(i=0;i<nvtxs;i++){
    DLA_INIT_HEAD(bnds + i);
  }  
    
  /**********************/
  /* compute boundaries */
  for(v=0;v<nvtxs;v++){
    prov = pro[v];

    for(j=0;j<nnpro;j++){
      procs[j] = 0;
      procon[j] = 0;
    }
    
    jstart = adjidx[v];
    jend   = adjidx[v+1];
    isbnd  = 0;

    for(j=jstart;j<jend;j++){
      w    = adjncy[j];
      prow = pro[w];
            
      if(prov != prow)
	isbnd = 1;
            
      procs[prow] += adjwgt[j];      
      procon[prow]++;
    }

    if(isbnd){
      nbbnds++;
      for(to=0;to<nnpro;to++){    
	if(procs[to] == 0 || to == prov)
	  continue;
	VREF_AddBnd(ctrl,v,prov,to);
      }
    }
  }

  return nbbnds;
}

/**
 * \brief Free all containers.
 *
 * \param ctrl uncoarsening ctrl structure
 */
void VREF_Free(RefCtrl* ctrl){
  int pro;
  int nnpro = ctrl->n->npro;
  vRefCont* proref = ctrl->proref;
  for(pro=0;pro<nnpro;pro++){
    CONT_Free(ctrl,proref + pro);
  }
}

/**
 * \brief initialize and allocate a container.
 *
 * \warning need to be free by CONT_Free()
 * \param ctrl uncoarsening ctrl structure
 * \param cont particular container to be initialized
 */
void CONT_Ini(RefCtrl* ctrl,vRefCont* cont){
  cont->arr = U_malloc(VREFCONTSIZE,vRef,"CONT_Ini code 2\n");
  cont->max_s = VREFCONTSIZE;
  cont->s     = 0;
}

/** 
 * \brief free a container.
 *
 * \param ctrl uncoarsening ctrl structure
 * \param cont particular container to be freed
 */
void CONT_Free(RefCtrl* ctrl,vRefCont* cont){
  U_free("CONT_Free code 1\n",&(cont->arr),LEND);  
}

/**
 * \brief Store passed information into container cont.
 *
 * \param ctrl uncoarsening ctrl structure
 * \param cont  container
 * \param idx,... information to be stored
 * \return pointer to the added element
 */
vRef* CONT_Add(RefCtrl* ctrl,vRefCont* cont,
	       int idx,int from,int to,double deltaF,double deltaT,double deltaO,int dOflag){
  vRef* old_arr = cont->arr;
  vRef* rvtx;
  int offset,i,size = cont->s;
  vRef* rinfo = cont->arr + cont->s;     

  rinfo->pos     = NULL;
  rinfo->idx     = idx;
  rinfo->from    = from;
  rinfo->to      = to;
  rinfo->deltaF  = deltaF;
  rinfo->deltaT  = deltaT;

  rinfo->deltaO  = deltaO;
  rinfo->dOflag  = dOflag;  
  DLA_APPEND(ctrl->bnds + idx,rinfo);    

  size = ++cont->s;

  if(cont->s >= cont->max_s){    
    /* realloc */
    cont->max_s += VREFCONTSIZE;
    cont->arr = U_realloc(cont->arr,cont->max_s,vRef,"CONT_Add code 1\n");

    /* update */
    offset    = cont->arr - old_arr;    

    for(i=0;i<size;i++){
      rvtx = cont->arr + i;
      if(rvtx->pos != NULL){
	*(rvtx->pos) = rvtx;
      }
      if(rvtx->n != NULL){
	if(rvtx->n < old_arr + size && rvtx->n >= old_arr)
	  rvtx->n += offset;
	else
	  rvtx->n->p += offset;
      }
      if(rvtx->p != NULL){
	if(rvtx->p < old_arr + size && rvtx->p >= old_arr)
	  rvtx->p += offset;
	else
	  rvtx->p->n += offset;
      }
    }    
    rinfo = cont->arr + size - 1;    
  }    
  return rinfo;
}

/**
 * \brief Add a given boundary reassignment to the correct container.
 *
 * store move information of vertex v from processor from to processor to
 * equires ctrl->procs to be accurate (vertex to processor connectivity)
 * \param ctrl uncoarsening ctrl structure
 * \param v vertex index to be added
 * \param from subdomain of the vertex (its processor)
 * \param to destination of the potential reassignement
 * \return pointer to added element
 */
vRef* VREF_AddBnd(RefCtrl* ctrl,int v,int from,int to){
  int*       procs = ctrl->procs;
  int*      procon = ctrl->procon;
  vRefCont* proref = ctrl->proref;   

  int*       gvwgt = ctrl->g->vwgt;

  int        nnpro = ctrl->n->npro;  
  double**   ratio = ctrl->n->ratio;
  int**    connect = ctrl->n->connect;
  int**    latency = ctrl->n->latency;
  double* prospeed = ctrl->n->prospeed;

  int dOflag;
  double deltaF,deltaT,deltaO;
    
  COMPUTEREFINFO(v,from,to,gvwgt,prospeed,procs,procon,nnpro,ratio,connect,latency,
		 deltaF,deltaT,deltaO,dOflag); 

  return CONT_Add(ctrl,proref + from,v,from,to,deltaF,deltaT,deltaO,dOflag);
}

/**
 * \brief remove an element from a container.
 *
 * remove rinfo from cont, if rinfo was in a dll return the next element of the
 * list
 * \param ctrl uncoarsening ctrl structure
 * \param cont container
 * \param rinfo pointer to the info to be removed
 * \return next entry of vertex vRef::idx in the container.
 */
vRef* CONT_Rem(RefCtrl* ctrl,vRefCont* cont,vRef* rinfo){ 
  vRef* next = DLA_NEXT(rinfo);
  vRef* last;

  ERROR((cont->s == 0,"CONT_Rem code 1 : this case should be impossible :S\n"));   

  DLA_REM_NEXT(rinfo,next,cont->arr,cont->s);
    
  if(rinfo->pos != NULL){
    ERROR((*(rinfo->pos) != cont->arr + cont->s,"CONT_Rem code 2 : wrong link curr vtx %d rvtxs vtx %d\n",cont->arr[cont->s].idx,(*(cont->arr[cont->s].pos))->idx));
    *(rinfo->pos) = rinfo;
  }
  
  last = cont->arr + cont->s;  
  last->pos     = NULL;
  last->idx     = -1;
  last->from    = -1;
  last->to      = -1;
  last->deltaF  = INFINITY;
  last->deltaT  = INFINITY;
  last->deltaO  = INFINITY;
  last->dOflag  = -1;
  DLA_INIT(last);  

  return next;
}
